package com.qianfeng.dbsm.admin.config;

import com.qianfeng.dbsm.admin.entity.TbMember;
import com.qianfeng.dbsm.admin.filter.JWTSecurityFilter;
import com.qianfeng.dbsm.admin.service.TbMemberService;
import com.qianfeng.dbsm.admin.util.JWTUtil;
import com.qianfeng.dbsm.admin.util.ResponseUtil;
import com.qianfeng.dbsm.common.ResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
//import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 功能说明
 *
 * @author 袁俊杰
 * @date 2022-11-18 19:42:47
 */
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    //主要就是对swagger进行放行的
    @Override
    public void configure(WebSecurity web) throws Exception {
        //放行swagger
        web.ignoring().antMatchers(HttpMethod.GET.toString(),
                "/v2/api-docs",
                "/swagger-resources",
                "/swagger-resources/**",
                "/configuration/ui",
                "/configuration/security",
                "/swagger-ui.html/**",
                "/doc.html/**",
                "/webjars/**");
    }

    @Autowired
    JWTSecurityFilter jwtSecurityFilter;

    @Autowired
    TbMemberService tbMemberService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(jwtSecurityFilter, UsernamePasswordAuthenticationFilter.class);
        //1，设置登录地址，登录的参数
        //2，处理登录成功的结果，登录失败的结果，没有登录的结果，和没有权限的结果
        http
                .authorizeRequests()//授权配置
                //在这条语句下面编写放行所要访问的路径
                .antMatchers("/t1").permitAll()
                .antMatchers("/productManager/queryProduct").permitAll()
                .anyRequest().authenticated() //需要权限验证
                //没有权限的处理
                .and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {

            public void handle(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse,
                               AccessDeniedException e) throws IOException, ServletException {
            }
        })
                .and()
                .formLogin() //登录认证配置
                .loginProcessingUrl("/tbMember/login.action").permitAll()//登陆请求
                //.failureUrl("/fail.html")//登录失败的访问页面
                .failureHandler(new AuthenticationFailureHandler() {

                    public void onAuthenticationFailure(
                            HttpServletRequest httpServletRequest,
                            HttpServletResponse httpServletResponse,
                            AuthenticationException e) throws IOException, ServletException {

                        //没有登录
                        ResponseData rd = ResponseData.fail("登录失败，原因：" + e.getMessage(), null);
                        ResponseUtil.writeJson(httpServletResponse, rd);
                    }
                })
                //.defaultSuccessUrl("/index.jhtml")//登录成功之后访问页面
                .successHandler(new AuthenticationSuccessHandler() {

                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                                        HttpServletResponse httpServletResponse,
                                                        Authentication authentication) throws IOException, ServletException {
                        //获取用户账号
                        UserDetails userDetails = (UserDetails) authentication.getPrincipal();


                        //生成短token
                        String token = JWTUtil.create(userDetails.getUsername());
                        Map<String, Object> map = new HashMap<String, Object>();
                        map.put("token", token);
                        //返回token给浏览器
                        ResponseData responseData = ResponseData.success("登录成功", map);

                        //生成长token
                        String ctoken = JWTUtil.create2(userDetails.getUsername());
                        //将长短token放入tokenmap中
                        JWTUtil.put(token, ctoken);


                        ResponseData rd = ResponseData.success("登录成功", map);

                        ResponseUtil.writeJson(httpServletResponse, rd);
                    }
                })
                .usernameParameter("username") //登录请求的参数必须是username
                .passwordParameter("password")

                //处理没有登录的情况
                .and().exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {

            public void commence(HttpServletRequest httpServletRequest,
                                 HttpServletResponse httpServletResponse,
                                 AuthenticationException e) throws IOException, ServletException {
                //没有登录
                ResponseData rd = ResponseData.fail("用户没有登录,请先登录", null);
                ResponseUtil.writeJson(httpServletResponse, rd);
            }
        })
                .and()
                .logout()
                .logoutUrl("/tbMember/logout").permitAll()//退出功能
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest,
                                                HttpServletResponse httpServletResponse,
                                                Authentication authentication) throws IOException, ServletException {
                        String token = httpServletRequest.getHeader("Authorization");
                        JWTUtil.logout(token);
                        Map<String,String> map = JWTUtil.getTokenMap();
                        ResponseData rd = ResponseData.success("退出登录成功", null);
                        ResponseUtil.writeJson(httpServletResponse, rd);
                    }
                })
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//不使用session
                .and().csrf().disable();//禁用csrf
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
        //创建自定义的操作dao
        DaoAuthenticationProvider authProvider
                = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);

        //设置密码编码加密
        authProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return authProvider;
    }
}
